#ifndef __I264E_COMMON_H__
#define __I264E_COMMON_H__

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <assert.h>

#include <i264e_osdep.h>

/****************************************************************************
 * Macros
 ****************************************************************************/
#define RAW_STREAM_OFFSET	      (4096 + 64 * 2)
#define IS_DISPOSABLE(type) ( type == I264E_TYPE_B )
#define I264E_THREAD_MAX	2		/* this is depend the icodec rd and enc buf max number */
#define I264E_BFRAME_MAX	0
#define I264E_REF_MAX	2

#define QP_BD_OFFSET	0
#define QP_MAX_SPEC		(51)
#define QP_MAX			(51)
#define QP_MAX_MAX		(51+2*6+18)

#define NALU_OVERHEAD 5 // startcode + NAL type costs 5 bytes per frame
#define FILLER_OVERHEAD (NALU_OVERHEAD+1)
#define SEI_OVERHEAD (NALU_OVERHEAD - (h->common.param.b_annexb && (h->cur->p.out.i_nal-1)))

typedef int16_t  dctcoef;
typedef uint16_t udctcoef;

#define CHROMA_SIZE(s) ((s)>>(1+1))

#define PIXEL_SPLAT_X4(x) ((x)*0x01010101U)
#define MPIXEL_X4(src) M32(src)

#define CPPIXEL_X4(dst,src) MPIXEL_X4(dst) = MPIXEL_X4(src)

#define I264E_SCAN8_LUMA_SIZE (5*8)
#define I264E_SCAN8_SIZE (I264E_SCAN8_LUMA_SIZE*3)
#define I264E_SCAN8_0 (4+1*8)

/* Scan8 organization:
 *    0 1 2 3 4 5 6 7
 * 0  DY    y y y y y
 * 1        y Y Y Y Y
 * 2        y Y Y Y Y
 * 3        y Y Y Y Y
 * 4        y Y Y Y Y
 * 5  DU    u u u u u
 * 6        u U U U U
 * 7        u U U U U
 * 8        u U U U U
 * 9        u U U U U
 * 10 DV    v v v v v
 * 11       v V V V V
 * 12       v V V V V
 * 13       v V V V V
 * 14       v V V V V
 * DY/DU/DV are for luma/chroma DC.
 */

#define LUMA_DC   48
#define CHROMA_DC 49

static const uint8_t i264e_scan8[16*3 + 3] =
{
    4+ 1*8, 5+ 1*8, 4+ 2*8, 5+ 2*8,
    6+ 1*8, 7+ 1*8, 6+ 2*8, 7+ 2*8,
    4+ 3*8, 5+ 3*8, 4+ 4*8, 5+ 4*8,
    6+ 3*8, 7+ 3*8, 6+ 4*8, 7+ 4*8,
    4+ 6*8, 5+ 6*8, 4+ 7*8, 5+ 7*8,
    6+ 6*8, 7+ 6*8, 6+ 7*8, 7+ 7*8,
    4+ 8*8, 5+ 8*8, 4+ 9*8, 5+ 9*8,
    6+ 8*8, 7+ 8*8, 6+ 9*8, 7+ 9*8,
    4+11*8, 5+11*8, 4+12*8, 5+12*8,
    6+11*8, 7+11*8, 6+12*8, 7+12*8,
    4+13*8, 5+13*8, 4+14*8, 5+14*8,
    6+13*8, 7+13*8, 6+14*8, 7+14*8,
    0+ 0*8, 0+ 5*8, 0+10*8
};

typedef struct i264e_thread i264e_thread_t;

#include <i264e.h>
#include <i264e_threadpool.h>
#include <i264e_cabac.h>
#include <i264e_bitstream.h>
#include <i264e_set.h>
#include <i264e_macroblock.h>
#include <i264e_frame.h>
#include <i264e_strdindex.h>
#include <i264e_iicodec.h>
#include <i264e_ratecontrol.h>
#include <i264e_reconfig.h>
#include <i264e_pixel.h>
#include <jzm_enc_api.h>
#include <jzm_enc_api_t20.h>
#include <jzm_enc_api_t21.h>
#include <jzm_enc_api_t30.h>
#define SPEC_QP(x)		C_MIN((x), QP_MAX_SPEC)

/*****************************************************************/
enum slice_type_e
{
    I264E_SLICE_TYPE_P  = 0,
    I264E_SLICE_TYPE_B  = 1,
    I264E_SLICE_TYPE_I  = 2,
};

static const char slice_type_to_char[] = { 'P', 'B', 'I' };

enum denoise_stage {
	DENOISE_STAGE_NONE			= 0,
	DENOISE_STAGE_ENC_EXT		= 1,
	DENOISE_STAGE_EXT_ENC		= 2,
};

struct i264e_weight_t;
typedef void (* weight_fn_t)( pixel *, intptr_t, pixel *,intptr_t, const struct i264e_weight_t *, int );

typedef struct i264e_weight_t
{
    /* aligning the first member is a gcc hack to force the struct to be
     * 16 byte aligned, as well as force sizeof(struct) to be a multiple of 16 */
    ALIGNED_16( int16_t cachea[8] );
    int16_t cacheb[8];
    int32_t i_denom;
    int32_t i_scale;
    int32_t i_offset;
    weight_fn_t *weightfn;
} ALIGNED_16( i264e_weight_t );

typedef struct
{
    i264e_sps_t *sps;
    i264e_pps_t *pps;

    int i_type;
    int i_first_mb;
    int i_last_mb;

    int i_pps_id;

    int i_frame;

    int b_mbaff;
    int b_field_pic;
    int b_bottom_field;

    int i_idr_pic_id;   /* -1 if nal_type != 5 */

    int i_poc;
    int i_delta_poc_bottom;

    int i_delta_poc[2];
    int i_redundant_pic_cnt;

    int b_direct_spatial_mv_pred;

    int b_num_ref_idx_override;
    int i_num_ref_idx_l0_active;
    int i_num_ref_idx_l1_active;

    int b_ref_pic_list_reordering[2];
    struct
    {
        int idc;
        int arg;
    } ref_pic_list_order[2][I264E_REF_MAX];

    /* P-frame weighting */
    int b_weighted_pred;
    i264e_weight_t weight[I264E_REF_MAX*2][3];

    int i_mmco_remove_from_end;
    int i_mmco_command_count;
    struct /* struct for future expansion */
    {
		int idc;
        int arg;
        int i_poc;
    } mmco[I264E_REF_MAX];

    int i_cabac_init_idc;

    int i_qp;
    int i_qp_delta;
    int b_sp_for_swidth;
    int i_qs_delta;

    /* deblocking filter */
    int i_disable_deblocking_filter_idc;
    int i_alpha_c0_offset;
    int i_beta_offset;
} i264e_slice_header_t;

typedef struct {
	int		i_frame;
	int		i_thread_phase;
	int		i_idr_pic_id;   /* -1 if nal_type != 5 */
	/*for roi rcfg */
	c_roi_t	roi[8];
} i264e_thread_input_t;

/* Current frame stats */
typedef struct
{
    /* Metrics */
    int64_t i_ssd[3];
} i264e_frame_stat_t;

typedef struct {
	int64_t     i_prev_duration; /* Duration of previous frame */
    /* stats */
    struct
    {
        /* Cumulated stats */

        /* per slice info */
        int     i_frame_count[3];
        int64_t i_frame_size[3];

		/* */
        double  f_ssd_global[3];
        double  f_psnr_average[3];
        double  f_psnr_mean_y[3];
        double  f_psnr_mean_u[3];
        double  f_psnr_mean_v[3];
        double  f_ssim_mean_y[3];
        double  f_frame_duration[3];
        /* */

    } stat;
} i264e_thread_output_t;

typedef struct {
	i264e_thread_t	*prev;
	i264e_thread_t	*cur;
	i264e_thread_t	*oldest;
} i264e_thread_info_t;

typedef struct {
	int		b_thread_active;
	int		b_thread_done;
	int		b_thread_used;
	int		b_oldest_thread_used;
	int		i_type;
	int		i_global_qp;
	int		b_thread_dnstage;
	int		i_poc;
	int		i_nal_type;
	int		i_nal_ref_idc;
	int		prev_i_frame;

	i264e_thread_info_t		threads_info;
	i264e_pthread_mutex_t	mutex;
	i264e_pthread_cond_t		cond;
	i264e_pthread_cond_t		cond_slice;

	i264e_frame_t	*fenc;
	i264e_frame_t	*fpad;
	i264e_frame_t	*fref;
	i264e_frame_t	*fmref;
	i264e_frame_t	*fdec;
	uint8_t			*fbs;
	unsigned int    *vpupri;
	i264e_cabac_t		cabac;
	i264e_slice_header_t	sh;
	i264e_icodec_io_t		icodec_io;
    /* bitstream output */
	struct {
		int         i_nal;
		int         i_nals_allocated;
		i264e_nal_t  *nal;
		int         i_bitstream;    /* size of p_bitstream */
		uint8_t     *p_bitstream;   /* will hold data for all nal */
		bs_t        bs;
	} out;

	uint8_t			*nal_buffer;
	int				nal_buffer_size;
	i264e_pic_t		pic_in;
	i264e_pic_t		pic_out;
	int				frame_size;

    struct
    {
        /* Current frame stats */
        i264e_frame_stat_t frame;
    } stat;
	/*for c2g backup*/
	uint8_t		*uvplane_bak;

    /* reenode tmp info */
    int             b_change_ref;       /* should be reset to zero */
    uint8_t         *tmp_bs_space;      /* shouldn't forget to free */
    int             tmp_bs_size;
    struct {
        i264e_frame_t   *old_fref;
        i264e_frame_t   *cur_fref;
        uint8_t         *old_bs;
        int             i_old_bslen;
        uint8_t         *cur_bs;
        int             i_cur_bslen;
    } reenc_tmp_info;

} i264e_thread_private_t;

typedef struct i264e_thread {
	i264e_thread_input_t		ip;
	i264e_thread_output_t	op;
	i264e_thread_output_t	opb[I264E_THREAD_MAX];
	i264e_thread_private_t	p;
	i264e_t					*h;
} i264e_thread_t;

typedef struct {
	i264e_param_t		param;
	VPU_RC_S_T21        eprc_t21;
	uint32_t			lastidr_bits;
	int					lastidr_qp;
	i264e_sps_t			sps[1];
	i264e_pps_t			pps[1];

	i264e_bitstream_function_t	bsf;
	c_mc_function_t			mc;

	i264e_skip_header_t	skip_h;

	i264e_icodec_t			*icodec;
	i264e_vbinfo_t		*vbinfo;
	i264e_frame_t		rdframe[I264E_THREAD_MAX < 2 ? 4 : I264E_THREAD_MAX * 2];
	int					rdframe_cnt;
	i264e_frame_t		rdsframe[I264E_THREAD_MAX < 2 ? 4 : I264E_THREAD_MAX * 2];
	int					rdsframe_cnt;
	i264e_frame_t		encframe[I264E_THREAD_MAX];
	int					encframe_cnt;
	i264e_frame_t		padframe[I264E_THREAD_MAX];
	int					padframe_cnt;
	i264e_frame_t		extframe[I264E_THREAD_MAX];

	i264e_threadpool_t	*threadpool;

	i264e_reconfig_t		reconfig;
    int             H264E_SliceInfoSize;
} i264e_common_t;

struct i264e {
	i264e_common_t		common;
	i264e_thread_t		thread;
	i264e_thread_t		*threads[I264E_THREAD_MAX];
	i264e_thread_t		*cur;
};

/*
 * i264e_param2string: return a (malloced) string containing most of
 * the encoding options
 */
extern char *i264e_param2string(i264e_param_t *p, int b_res);

/* log */
extern void i264e_log(i264e_t *h, int i_level, const char *psz_fmt, ... );
extern int i264e_validate_parameters(i264e_t *h);

extern void i264e_cabac_init(i264e_t *h);
extern void i264e_threadpool_wait_all(i264e_t *h);
extern int i264e_slice_init(i264e_thread_t *t);
extern void i264e_nal_start(i264e_thread_t *t, int i_type, int i_ref_idc);
extern int i264e_nal_end(i264e_thread_t *t);

#define i264e_check(expr, ...) if (!(expr)) { \
	i264e_log(NULL, C_LOG_ERROR, __VA_ARGS__); \
	abort(); \
}

#endif
